/*
 * Copyright (c) 2018 Hitevision
 */

package tw.com.hitevision.whiteboard.android;

import android.graphics.Canvas;
import android.os.Build;
import android.util.Log;
import android.view.SurfaceHolder;

import tw.com.hitevision.whiteboard.android.fastdraw.FastDraw;

public class RenderThread extends Thread {
    private static final String TAG = RenderThread.class.getSimpleName();

    private static boolean logging = false;

    private final static int TARGET_FPS = 60;
    private final static int FRAME_PERIOD = 1000 / TARGET_FPS;

    private final Renderer renderer;
    private final SurfaceHolder holder;

    private volatile boolean running;
    private volatile boolean isPaused;
    private volatile boolean invalidated;

    // added by Yuda
    private volatile long startRecord;
    private volatile boolean isLocked;
    private RendererDebugListener debugListener;
    private FastDraw fastDraw;

    RenderThread(Renderer r, SurfaceHolder h,FastDraw fastDraw) {
        super("RenderThread");

        renderer = r;
        holder = h;
        running = false;
        isPaused = false;
        invalidated = true;

        final Invalidator invalidator = () -> invalidated = true;
        renderer.onInvalidatorCreated(invalidator);

        this.fastDraw = fastDraw;
    }

    void setRunning(boolean run) {
        running = run;
        invalidated = true;
        if (debugListener != null && !run) {
            debugListener.onThreadStop();
        }
    }

    void setPaused(boolean isPaused) {
        this.isPaused = isPaused;

        if (!isPaused) {
            invalidated = true;
        }
    }

    @Override
    public void run() {
        Canvas c = null;

        long start;
        long renderTime;
        long elapsed;
        long processTime;
        long processStart;
        long processElapsed;
        long sleepTime;
        long lastTime = 0;
        long time;

        while (running) {
            time = System.currentTimeMillis();
            // added by Yuda
            if (debugListener != null) {
                debugListener.onRendererStatus(System.currentTimeMillis());
            }

            if(Props.getFlavorChipset().equals(Props.Flavor.Chipset.Mstar) && fastDraw.isDrawing() && (!FastDraw.needDraw && time - lastTime < 100)){
                continue;
            }
            FastDraw.needDraw = false;
            lastTime = time;

            if (isPaused) {
                // paused, skip rendering and just sleep for frame period
                try {
//                    log("-   pause");
                    Thread.sleep(FRAME_PERIOD);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            }

            start = System.currentTimeMillis();

            startRecord = start;

            // render to screen

            renderer.preRender();

            if (invalidated) {
//                log("-   run: invalidated");
                invalidated = false;
                try {
                    isLocked = true;

//                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O&&!Props.getFlavorChipset().equals(Props.Flavor.Chipset.Optoma)) {
//                        c = holder.lockHardwareCanvas();
//                    } else {
                        c = holder.lockCanvas();
//                    }

                    isLocked = false;

//                    log("-   canvas locked");
                    synchronized (holder) {
                        if (c != null) {
                            renderer.render(c);
                        } else {
                            try {
                                log("-   canvas lock failed");
                                Thread.sleep(1);
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                } finally {
                    if (c != null) {
                        holder.unlockCanvasAndPost(c);
//                        log("-   canvas unlocked");
                    }
                }
            }

            renderer.postRender();

            renderTime = System.currentTimeMillis() - start;
            if (fastDraw != null && FastDraw.isSupportedCanvasFastDraw()) {
                fastDraw.setRenderTime(renderTime);
            }

            processTime = FRAME_PERIOD - renderTime;
//            log("-   renderTime=" + renderTime + ", processTime=" + processTime);
            if (processTime >= 0) {
                // use extra time left to process queued events
                processElapsed = 0;
                processStart = System.currentTimeMillis();
                while (processElapsed < processTime) {
                    log("-   process");
                    final boolean result = renderer.process();
                    if (!result) {
                        break;
                    }

                    processElapsed = System.currentTimeMillis() - processStart;
                }

                sleepTime = processTime - processElapsed;
//                log("-   sleepTime=" + sleepTime);
                if (sleepTime > 0) {
                    // sleep for rest of frame period
                    try {
                        log("-   sleep for " + sleepTime);
                        Thread.sleep(sleepTime);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } else {
                // need to skip frames and catch up on processing
//                log("-   skipped frames=" + (-processTime / FRAME_PERIOD + 1));
                processTime = -processTime % FRAME_PERIOD;
                if (processTime == 0) {
                    processTime = FRAME_PERIOD;
                }
//                processTime = -processTime;
                processElapsed = 0;
                processStart = System.currentTimeMillis();
                while (processElapsed < processTime) {
                    log("-   process");
                    final boolean result = renderer.process();
                    if (!result) {
                        break;
                    }

                    processElapsed = System.currentTimeMillis() - processStart;
                }
//                log("-   processElapsed=" + processElapsed);
            }
            elapsed = System.currentTimeMillis() - start;
//            log("- elapsed=" + elapsed);
        }
    }

    public interface Renderer {
        void onInvalidatorCreated(Invalidator invalidator);

        /**
         * Processes next queued event.
         *
         * @return {@code true} when done, otherwise {@code false} if no events queued
         */
        boolean process();

        void preRender();

        void postRender();

        void render(Canvas canvas);
    }

    public interface Invalidator {
        void invalidate();
    }

    // add by Yuda
    boolean isRendering() {
        return (running && !isPaused);
    }

    // add by Yuda
    boolean isLocked() {
        return isLocked;
    }

    // add by Yuda
    long getStartRecord() {
        return startRecord;
    }

    // add by Yuda
    public void setDebugListener(RendererDebugListener debugListener) {
        this.debugListener = debugListener;
        logging = debugListener != null;
    }


    private void log(String msg) {
        if (logging) {
            Log.d(TAG, msg);
        }
    }

    // add by Yuda
    public interface RendererDebugListener {

        void onRendererStatus(long time);

        void onThreadStop();

    }

}
